/**
* WYSIWYG - jQuery plugin 0.97
* (0.97.2 - From infinity)
*
* Copyright (c) 2008-2009 Juan M Martinez, 2010-2011 Akzhan Abdulin and all contributors
* https://github.com/akzhan/jwysiwyg
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
/*jslint browser: true, forin: true */
(function ($) {
"use strict";
/* Wysiwyg namespace: private properties and methods */
var console = window.console ? window.console : {
log: $.noop,
error: function (msg) {
$.error(msg);
}
};
var supportsProp = (('prop' in $.fn) && ('removeProp' in $.fn));
function Wysiwyg() {
// - the item is added by this.ui.appendControls and then appendItem
// - click triggers this.triggerControl
// cmd or[key] - designMode exec function name
// tags - activates control for these tags (@see checkTargets)
// css - activates control if one of css is applied
this.controls = {
bold: {
groupIndex: 0,
visible: true,
tags: ["b", "strong"],
css: {
fontWeight: "bold"
},
tooltip: "Bold",
hotkey: {"ctrl": 1, "key": 66}
},
copy: {
groupIndex: 8,
visible: false,
tooltip: "Copy"
},
createLink: {
groupIndex: 6,
visible: true,
exec: function () {
var self = this;
if ($.wysiwyg.controls && $.wysiwyg.controls.link) {
$.wysiwyg.controls.link.init(this);
} else if ($.wysiwyg.autoload) {
$.wysiwyg.autoload.control("wysiwyg.link.js", function () {
self.controls.createLink.exec.apply(self);
});
} else {
console.error("$.wysiwyg.controls.link not defined. You need to include wysiwyg.link.js file");
}
},
tags: ["a"],
tooltip: "Create link"
},
cut: {
groupIndex: 8,
visible: false,
tooltip: "Cut"
},
decreaseFontSize: {
groupIndex: 9,
visible: false,
tags: ["small"],
tooltip: "Decrease font size",
exec: function () {
this.decreaseFontSize();
}
},
h1: {
groupIndex: 7,
visible: true,
className: "h1",
command: ($.browser.msie || $.browser.safari) ? "FormatBlock" : "heading",
"arguments": ($.browser.msie || $.browser.safari) ? "<h1>" : "h1",
tags: ["h1"],
tooltip: "Header 1"
},
h2: {
groupIndex: 7,
visible: true,
className: "h2",
command: ($.browser.msie || $.browser.safari) ? "FormatBlock" : "heading",
"arguments": ($.browser.msie || $.browser.safari) ? "<h2>" : "h2",
tags: ["h2"],
tooltip: "Header 2"
},
h3: {
groupIndex: 7,
visible: true,
className: "h3",
command: ($.browser.msie || $.browser.safari) ? "FormatBlock" : "heading",
"arguments": ($.browser.msie || $.browser.safari) ? "<h3>" : "h3",
tags: ["h3"],
tooltip: "Header 3"
},
highlight: {
tooltip: "Highlight",
className: "highlight",
groupIndex: 1,
visible: false,
css: {
backgroundColor: "rgb(255, 255, 102)"
},
exec: function () {
var command, node, selection, args;
if ($.browser.msie || $.browser.safari) {
command = "backcolor";
} else {
command = "hilitecolor";
}
if ($.browser.msie) {
node = this.getInternalRange().parentElement();
} else {
selection = this.getInternalSelection();
node = selection.extentNode || selection.focusNode;
while (node.style === undefined) {
node = node.parentNode;
if (node.tagName && node.tagName.toLowerCase() === "body") {
return;
}
}
}
if (node.style.backgroundColor === "rgb(255, 255, 102)" ||
node.style.backgroundColor === "#ffff66") {
args = "#ffffff";
} else {
args = "#ffff66";
}
this.editorDoc.execCommand(command, false, args);
}
},
html: {
groupIndex: 10,
visible: false,
exec: function () {
var elementHeight;
if (this.options.resizeOptions && $.fn.resizable) {
elementHeight = this.element.height();
}
if (this.viewHTML) { //textarea is shown
this.setContent(this.original.value);
$(this.original).hide();
this.editor.show();
if (this.options.resizeOptions && $.fn.resizable) {
// if element.height still the same after frame was shown
if (elementHeight === this.element.height()) {
this.element.height(elementHeight + this.editor.height());
}
this.element.resizable($.extend(true, {
alsoResize: this.editor
}, this.options.resizeOptions));
}
this.ui.toolbar.find("li").each(function () {
var li = $(this);
if (li.hasClass("html")) {
li.removeClass("active");
} else {
li.removeClass('disabled');
}
});
} else { //wysiwyg is shown
this.saveContent();
$(this.original).css({
width: this.element.outerWidth() - 6,
height: this.element.height() - this.ui.toolbar.height() - 6,
resize: "none"
}).show();
this.editor.hide();
if (this.options.resizeOptions && $.fn.resizable) {
// if element.height still the same after frame was hidden
if (elementHeight === this.element.height()) {
this.element.height(this.ui.toolbar.height());
}
this.element.resizable("destroy");
}
this.ui.toolbar.find("li").each(function () {
var li = $(this);
if (li.hasClass("html")) {
li.addClass("active");
} else {
if (false === li.hasClass("fullscreen")) {
li.removeClass("active").addClass('disabled');
}
}
});
}
this.viewHTML = !(this.viewHTML);
},
tooltip: "View source code"
},
increaseFontSize: {
groupIndex: 9,
visible: false,
tags: ["big"],
tooltip: "Increase font size",
exec: function () {
this.increaseFontSize();
}
},
indent: {
groupIndex: 2,
visible: true,
tooltip: "Indent"
},
insertHorizontalRule: {
groupIndex: 6,
visible: true,
tags: ["hr"],
tooltip: "Insert Horizontal Rule"
},
insertImage: {
groupIndex: 6,
visible: true,
exec: function () {
var self = this;
if ($.wysiwyg.controls && $.wysiwyg.controls.image) {
$.wysiwyg.controls.image.init(this);
} else if ($.wysiwyg.autoload) {
$.wysiwyg.autoload.control("wysiwyg.image.js", function () {
self.controls.insertImage.exec.apply(self);
});
} else {
console.error("$.wysiwyg.controls.image not defined. You need to include wysiwyg.image.js file");
}
},
tags: ["img"],
tooltip: "Insert image"
},
insertOrderedList: {
groupIndex: 5,
visible: true,
tags: ["ol"],
tooltip: "Insert Ordered List"
},
insertTable: {
groupIndex: 6,
visible: true,
exec: function () {
var self = this;
if ($.wysiwyg.controls && $.wysiwyg.controls.table) {
$.wysiwyg.controls.table(this);
} else if ($.wysiwyg.autoload) {
$.wysiwyg.autoload.control("wysiwyg.table.js", function () {
self.controls.insertTable.exec.apply(self);
});
} else {
console.error("$.wysiwyg.controls.table not defined. You need to include wysiwyg.table.js file");
}
},
tags: ["table"],
tooltip: "Insert table"
},
insertUnorderedList: {
groupIndex: 5,
visible: true,
tags: ["ul"],
tooltip: "Insert Unordered List"
},
italic: {
groupIndex: 0,
visible: true,
tags: ["i", "em"],
css: {
fontStyle: "italic"
},
tooltip: "Italic",
hotkey: {"ctrl": 1, "key": 73}
},
justifyCenter: {
groupIndex: 1,
visible: true,
tags: ["center"],
css: {
textAlign: "center"
},
tooltip: "Justify Center"
},
justifyFull: {
groupIndex: 1,
visible: true,
css: {
textAlign: "justify"
},
tooltip: "Justify Full"
},
justifyLeft: {
visible: true,
groupIndex: 1,
css: {
textAlign: "left"
},
tooltip: "Justify Left"
},
justifyRight: {
groupIndex: 1,
visible: true,
css: {
textAlign: "right"
},
tooltip: "Justify Right"
},
ltr: {
groupIndex: 10,
visible: false,
exec: function () {
var p = this.dom.getElement("p");
if (!p) {
return false;
}
$(p).attr("dir", "ltr");
return true;
},
tooltip : "Left to Right"
},
outdent: {
groupIndex: 2,
visible: true,
tooltip: "Outdent"
},
paragraph: {
groupIndex: 7,
visible: false,
className: "paragraph",
command: "FormatBlock",
"arguments": ($.browser.msie || $.browser.safari) ? "<p>" : "p",
tags: ["p"],
tooltip: "Paragraph"
},
paste: {
groupIndex: 8,
visible: false,
tooltip: "Paste"
},
redo: {
groupIndex: 4,
visible: true,
tooltip: "Redo"
},
removeFormat: {
groupIndex: 10,
visible: true,
exec: function () {
this.removeFormat();
},
tooltip: "Remove formatting"
},
rtl: {
groupIndex: 10,
visible: false,
exec: function () {
var p = this.dom.getElement("p");
if (!p) {
return false;
}
$(p).attr("dir", "rtl");
return true;
},
tooltip : "Right to Left"
},
strikeThrough: {
groupIndex: 0,
visible: true,
tags: ["s", "strike"],
css: {
textDecoration: "line-through"
},
tooltip: "Strike-through"
},
subscript: {
groupIndex: 3,
visible: true,
tags: ["sub"],
tooltip: "Subscript"
},
superscript: {
groupIndex: 3,
visible: true,
tags: ["sup"],
tooltip: "Superscript"
},
underline: {
groupIndex: 0,
visible: true,
tags: ["u"],
css: {
textDecoration: "underline"
},
tooltip: "Underline",
hotkey: {"ctrl": 1, "key": 85}
},
undo: {
groupIndex: 4,
visible: true,
tooltip: "Undo"
},
code: {
visible : true,
groupIndex: 6,
tooltip: "Code snippet",
exec: function () {
var range = this.getInternalRange(),
common = $(range.commonAncestorContainer),
$nodeName = range.commonAncestorContainer.nodeName.toLowerCase();
if (common.parent("code").length) {
common.unwrap();
} else {
if ($nodeName !== "body") {
common.wrap("<code/>");
}
}
}
},
cssWrap: {
visible : false,
groupIndex: 6,
tooltip: "CSS Wrapper",
exec: function () {
$.wysiwyg.controls.cssWrap.init(this);
}
}
};
this.defaults = {
html: '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" style="margin:0"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></head><body style="margin:0; padding: 0 5px; font-size: 12px; font-family: \'Lucida Sans Unicde\', \'Lucida Grande\', Arial, Helvetica, sans-serif;">INITIAL_CONTENT</body></html>',
debug: false,
controls: {},
css: {},
events: {},
autoGrow: false,
autoSave: true,
brIE: true, // http://code.google.com/p/jwysiwyg/issues/detail?id=15
formHeight: 270,
formWidth: 440,
iFrameClass: null,
initialContent: "<p>Initial content</p>",
maxHeight: 10000, // see autoGrow
maxLength: 0,
messages: {
nonSelection: "Select the text you wish to link"
},
toolbarHtml: '<ul role="menu" class="toolbar"></ul>',
removeHeadings: false,
replaceDivWithP: false,
resizeOptions: false,
rmUnusedControls: false, // https://github.com/akzhan/jwysiwyg/issues/52
rmUnwantedBr: true, // http://code.google.com/p/jwysiwyg/issues/detail?id=11
tableFiller: "Lorem ipsum",
initialMinHeight: null,
controlImage: {
forceRelativeUrls: false
},
controlLink: {
forceRelativeUrls: false
},
plugins: { // placeholder for plugins settings
autoload: false,
i18n: false,
rmFormat: {
rmMsWordMarkup: false
}
},
dialog : "default"
};
//these properties are set from control hashes
this.availableControlProperties = [
"arguments",
"callback",
"className",
"command",
"css",
"custom",
"exec",
"groupIndex",
"hotkey",
"icon",
"tags",
"tooltip",
"visible"
];
this.editor = null; //jquery iframe holder
this.editorDoc = null;
this.element = null;
this.options = {};
this.original = null;
this.savedRange = null;
this.timers = [];
this.validKeyCodes = [8, 9, 13, 16, 17, 18, 19, 20, 27, 33, 34, 35, 36, 37, 38, 39, 40, 45, 46];
this.isDestroyed = false;
this.dom = { // DOM related properties and methods
ie: {
parent: null // link to dom
},
w3c: {
parent: null // link to dom
}
};
this.dom.parent = this;
this.dom.ie.parent = this.dom;
this.dom.w3c.parent = this.dom;
this.ui = {}; // UI related properties and methods
this.ui.self = this;
this.ui.toolbar = null;
this.ui.initialHeight = null; // ui.grow
this.dom.getAncestor = function (element, filterTagName) {
filterTagName = filterTagName.toLowerCase();
while (element && typeof element.tagName != "undefined" && "body" !== element.tagName.toLowerCase()) {
if (filterTagName === element.tagName.toLowerCase()) {
return element;
}
element = element.parentNode;
}
if(!element.tagName && (element.previousSibling || element.nextSibling)) {
if(element.previousSibling) {
if(element.previousSibling.tagName.toLowerCase() == filterTagName) {
return element.previousSibling;
}
}
if(element.nextSibling) {
if(element.nextSibling.tagName.toLowerCase() == filterTagName) {
return element.nextSibling;
}
}
}
return null;
};
this.dom.getElement = function (filterTagName) {
var dom = this;
filterTagName = filterTagName.toLowerCase();
if (window.getSelection) {
return dom.w3c.getElement(filterTagName);
} else {
return dom.ie.getElement(filterTagName);
}
};
this.dom.ie.getElement = function (filterTagName) {
var dom = this.parent,
selection = dom.parent.getInternalSelection(),
range = selection.createRange(),
element;
if ("Control" === selection.type) {
// control selection
if (1 === range.length) {
element = range.item(0);
} else {
// multiple control selection
return null;
}
} else {
element = range.parentElement();
}
return dom.getAncestor(element, filterTagName);
};
this.dom.w3c.getElement = function (filterTagName) {
var dom = this.parent,
range = dom.parent.getInternalRange(),
element;
if (!range) {
return null;
}
element = range.commonAncestorContainer;
if (3 === element.nodeType) {
element = element.parentNode;
}
// if startContainer not Text, Comment, or CDATASection element then
// startOffset is the number of child nodes between the start of the
// startContainer and the boundary point of the Range
if (element === range.startContainer) {
element = element.childNodes[range.startOffset];
}
if(!element.tagName && (element.previousSibiling || element.nextSibling)) {
if(element.previousSibiling) {
if(element.previousSibiling.tagName.toLowerCase() == filterTagName) {
return element.previousSibiling;
}
}
if(element.nextSibling) {
if(element.nextSibling.tagName.toLowerCase() == filterTagName) {
return element.nextSibling;
}
}
}
return dom.getAncestor(element, filterTagName);
};
this.ui.addHoverClass = function () {
$(this).addClass("wysiwyg-button-hover");
};
this.ui.appendControls = function () {
var ui = this,
self = this.self,
controls = self.parseControls(),
hasVisibleControls = true, // to prevent separator before first item
groups = [],
controlsByGroup = {},
i,
currentGroupIndex, // jslint wants all vars at top of function
iterateGroup = function (controlName, control) { //called for every group when adding
if (control.groupIndex && currentGroupIndex !== control.groupIndex) {
currentGroupIndex = control.groupIndex;
hasVisibleControls = false;
}
if (!control.visible) {
return;
}
if (!hasVisibleControls) {
ui.appendItemSeparator();
hasVisibleControls = true;
}
if (control.custom) {
ui.appendItemCustom(controlName, control);
} else {
ui.appendItem(controlName, control);
}
};
$.each(controls, function (name, c) { //sort by groupIndex
var index = "empty";
if (undefined !== c.groupIndex) {
if ("" === c.groupIndex) {
index = "empty";
} else {
index = c.groupIndex;
}
}
if (undefined === controlsByGroup[index]) {
groups.push(index);
controlsByGroup[index] = {};
}
controlsByGroup[index][name] = c;
});
groups.sort(function (a, b) { //just sort group indexes by
if ("number" === typeof (a) && typeof (a) === typeof (b)) {
return (a - b);
} else {
a = a.toString();
b = b.toString();
if (a > b) {
return 1;
}
if (a === b) {
return 0;
}
return -1;
}
});
if (0 < groups.length) {
// set to first index in groups to proper placement of separator
currentGroupIndex = groups[0];
}
for (i = 0; i < groups.length; i += 1) {
$.each(controlsByGroup[groups[i]], iterateGroup);
}
};
this.ui.appendItem = function (name, control) {
var self = this.self,
className = control.className || control.command || name || "empty",
tooltip = control.tooltip || control.command || name || "";
return $('<li role="menuitem" unselectable="on">' + (className) + "</li>")
.addClass(className)
.attr("title", tooltip)
.hover(this.addHoverClass, this.removeHoverClass)
.click(function (event) {
if ($(this).hasClass("disabled")) {
return false;
}
self.triggerControl.apply(self, [name, control]);
/**
* @link https://github.com/akzhan/jwysiwyg/issues/219
*/
var $target = $(event.target);
for (var controlName in self.controls) {
if ($target.hasClass(controlName)) {
self.ui.toolbar.find("." + controlName).toggleClass("active");
self.editorDoc.rememberCommand = true;
break;
}
}
this.blur();
self.ui.returnRange();
self.ui.focus();
return true;
})
.appendTo(self.ui.toolbar);
};
this.ui.appendItemCustom = function (name, control) {
var self = this.self,
tooltip = control.tooltip || control.command || name || "";
if (control.callback) {
$(window).bind("trigger-" + name + ".wysiwyg", control.callback);
}
return $('<li role="menuitem" unselectable="on" style="background: url(\'' + control.icon + '\') no-repeat;"></li>')
.addClass("custom-command-" + name)
.addClass("wysiwyg-custom-command")
.addClass(name)
.attr("title", tooltip)
.hover(this.addHoverClass, this.removeHoverClass)
.click(function () {
if ($(this).hasClass("disabled")) {
return false;
}
self.triggerControl.apply(self, [name, control]);
this.blur();
self.ui.returnRange();
self.ui.focus();
self.triggerControlCallback(name);
return true;
})
.appendTo(self.ui.toolbar);
};
this.ui.appendItemSeparator = function () {
var self = this.self;
return $('<li role="separator" class="separator"></li>').appendTo(self.ui.toolbar);
};
this.autoSaveFunction = function () {
this.saveContent();
};
//called after click in wysiwyg "textarea"
this.ui.checkTargets = function (element) {
var self = this.self;
//activate controls
$.each(self.options.controls, function (name, control) {
var className = control.className || control.command || name || "empty",
tags,
elm,
css,
el,
checkActiveStatus = function (cssProperty, cssValue) {
var handler;
if ("function" === typeof (cssValue)) {
handler = cssValue;
if (handler(el.css(cssProperty).toString().toLowerCase(), self)) {
self.ui.toolbar.find("." + className).addClass("active");
}
} else {
if (el.css(cssProperty).toString().toLowerCase() === cssValue) {
self.ui.toolbar.find("." + className).addClass("active");
}
}
};
if ("fullscreen" !== className) {
self.ui.toolbar.find("." + className).removeClass("active");
}
//activate by allowed tags
if (control.tags || (control.options && control.options.tags)) {
tags = control.tags || (control.options && control.options.tags);
elm = element;
while (elm) {
if (elm.nodeType !== 1) {
break;
}
if ($.inArray(elm.tagName.toLowerCase(), tags) !== -1) {
self.ui.toolbar.find("." + className).addClass("active");
}
elm = elm.parentNode;
}
}
//activate by supposed css
if (control.css || (control.options && control.options.css)) {
css = control.css || (control.options && control.options.css);
el = $(element);
while (el) {
if (el[0].nodeType !== 1) {
break;
}
$.each(css, checkActiveStatus);
el = el.parent();
}
}
});
};
this.ui.designMode = function () {
var attempts = 3,
self = this.self,
runner;
runner = function (attempts) {
if ("on" === self.editorDoc.designMode) {
if (self.timers.designMode) {
window.clearTimeout(self.timers.designMode);
}
// IE needs to reget the document element (this.editorDoc) after designMode was set
if (self.innerDocument() !== self.editorDoc) {
self.ui.initFrame();
}
return;
}
try {
self.editorDoc.designMode = "on";
} catch (e) {
}
attempts -= 1;
if (attempts > 0) {
self.timers.designMode = window.setTimeout(function () { runner(attempts); }, 100);
}
};
runner(attempts);
};
this.destroy = function () {
this.isDestroyed = true;
var i, $form = this.element.closest("form");
for (i = 0; i < this.timers.length; i += 1) {
window.clearTimeout(this.timers[i]);
}
// Remove bindings
$form.unbind(".wysiwyg");
this.element.remove();
$.removeData(this.original, "wysiwyg");
$(this.original).show();
return this;
};
this.getRangeText = function () {
var r = this.getInternalRange();
if (r.toString) {
r = r.toString();
} else if (r.text) { // IE
r = r.text;
}
return r;
};
//not used?
this.execute = function (command, arg) {
if (typeof (arg) === "undefined") {
arg = null;
}
this.editorDoc.execCommand(command, false, arg);
};
this.extendOptions = function (options) {
var controls = {};
/**
* If the user set custom controls, we catch it, and merge with the
* defaults controls later.
*/
if ("object" === typeof options.controls) {
controls = options.controls;
delete options.controls;
}
options = $.extend(true, {}, this.defaults, options);
options.controls = $.extend(true, {}, controls, this.controls, controls);
if (options.rmUnusedControls) {
$.each(options.controls, function (controlName) {
if (!controls[controlName]) {
delete options.controls[controlName];
}
});
}
return options;
};
this.ui.focus = function () {
var self = this.self;
self.editor.get(0).contentWindow.focus();
return self;
};
this.ui.returnRange = function () {
var self = this.self, sel;
if (self.savedRange !== null) {
if (window.getSelection) { //non IE and there is already a selection
sel = window.getSelection();
if (sel.rangeCount > 0) {
sel.removeAllRanges();
}
try {
sel.addRange(self.savedRange);
} catch (e) {
console.error(e);
}
} else if (window.document.createRange) { // non IE and no selection
window.getSelection().addRange(self.savedRange);
} else if (window.document.selection) { //IE
self.savedRange.select();
}
self.savedRange = null;
}
};
this.increaseFontSize = function () {
if ($.browser.mozilla || $.browser.opera) {
this.editorDoc.execCommand("increaseFontSize", false, null);
} else if ($.browser.safari) {
var Range = this.getInternalRange(),
Selection = this.getInternalSelection(),
newNode = this.editorDoc.createElement("big");
// If cursor placed on text node
if (true === Range.collapsed && 3 === Range.commonAncestorContainer.nodeType) {
var text = Range.commonAncestorContainer.nodeValue.toString(),
start = text.lastIndexOf(" ", Range.startOffset) + 1,
end = (-1 === text.indexOf(" ", Range.startOffset)) ? text : text.indexOf(" ", Range.startOffset);
Range.setStart(Range.commonAncestorContainer, start);
Range.setEnd(Range.commonAncestorContainer, end);
Range.surroundContents(newNode);
Selection.addRange(Range);
} else {
Range.surroundContents(newNode);
Selection.removeAllRanges();
Selection.addRange(Range);
}
} else {
console.error("Internet Explorer?");
}
};
this.decreaseFontSize = function () {
if ($.browser.mozilla || $.browser.opera) {
this.editorDoc.execCommand("decreaseFontSize", false, null);
} else if ($.browser.safari) {
var Range = this.getInternalRange(),
Selection = this.getInternalSelection(),
newNode = this.editorDoc.createElement("small");
// If cursor placed on text node
if (true === Range.collapsed && 3 === Range.commonAncestorContainer.nodeType) {
var text = Range.commonAncestorContainer.nodeValue.toString(),
start = text.lastIndexOf(" ", Range.startOffset) + 1,
end = (-1 === text.indexOf(" ", Range.startOffset)) ? text : text.indexOf(" ", Range.startOffset);
Range.setStart(Range.commonAncestorContainer, start);
Range.setEnd(Range.commonAncestorContainer, end);
Range.surroundContents(newNode);
Selection.addRange(Range);
} else {
Range.surroundContents(newNode);
Selection.removeAllRanges();
Selection.addRange(Range);
}
} else {
console.error("Internet Explorer?");
}
};
this.getContent = function () {
if (this.viewHTML) {
this.setContent(this.original.value);
}
return this.events.filter('getContent', this.editorDoc.body.innerHTML);
};
/**
* A jWysiwyg specific event system.
*
* Example:
*
* $("#editor").getWysiwyg().events.bind("getContent", function (orig) {
* return "<div id='content'>"+orgi+"</div>";
* });
*
* This makes it so that when ever getContent is called, it is wrapped in a div#content.
*/
this.events = {
_events : {},
/**
* Similar to jQuery's bind, but for jWysiwyg only.
*/
bind : function (eventName, callback) {
if (typeof (this._events.eventName) !== "object") {
this._events[eventName] = [];
}
this._events[eventName].push(callback);
},
/**
* Similar to jQuery's trigger, but for jWysiwyg only.
*/
trigger : function (eventName, args) {
if (typeof (this._events.eventName) === "object") {
var editor = this.editor;
$.each(this._events[eventName], function (k, v) {
if (typeof (v) === "function") {
v.apply(editor, args);
}
});
}
},
/**
* This "filters" `originalText` by passing it as the first argument to every callback
* with the name `eventName` and taking the return value and passing it to the next function.
*
* This function returns the result after all the callbacks have been applied to `originalText`.
*/
filter : function (eventName, originalText) {
if (typeof (this._events[eventName]) === "object") {
var editor = this.editor,
args = Array.prototype.slice.call(arguments, 1);
$.each(this._events[eventName], function (k, v) {
if (typeof (v) === "function") {
originalText = v.apply(editor, args);
}
});
}
return originalText;
}
};
this.getElementByAttributeValue = function (tagName, attributeName, attributeValue) {
var i, value, elements = this.editorDoc.getElementsByTagName(tagName);
for (i = 0; i < elements.length; i += 1) {
value = elements[i].getAttribute(attributeName);
if ($.browser.msie) {
/** IE add full path, so I check by the last chars. */
value = value.substr(value.length - attributeValue.length);
}
if (value === attributeValue) {
return elements[i];
}
}
return false;
};
this.getInternalRange = function () {
var selection = this.getInternalSelection();
if (!selection) {
return null;
}
if (selection.rangeCount && selection.rangeCount > 0) { // w3c
return selection.getRangeAt(0);
} else if (selection.createRange) { // ie
return selection.createRange();
}
return null;
};
this.getInternalSelection = function () {
// firefox: document.getSelection is deprecated
if (this.editor.get(0).contentWindow) {
if (this.editor.get(0).contentWindow.getSelection) {
return this.editor.get(0).contentWindow.getSelection();
}
if (this.editor.get(0).contentWindow.selection) {
return this.editor.get(0).contentWindow.selection;
}
}
if (this.editorDoc.getSelection) {
return this.editorDoc.getSelection();
}
if (this.editorDoc.selection) {
return this.editorDoc.selection;
}
return null;
};
this.getRange = function () {
var selection = this.getSelection();
if (!selection) {
return null;
}
if (selection.rangeCount && selection.rangeCount > 0) { // w3c
selection.getRangeAt(0);
} else if (selection.createRange) { // ie
return selection.createRange();
}
return null;
};
this.getSelection = function () {
return (window.getSelection) ? window.getSelection() : window.document.selection;
};
// :TODO: you can type long string and letters will be hidden because of overflow
this.ui.grow = function () {
var self = this.self,
innerBody = $(self.editorDoc.body),
innerHeight = $.browser.msie ? innerBody[0].scrollHeight : innerBody.height() + 2 + 20, // 2 - borders, 20 - to prevent content jumping on grow
minHeight = self.ui.initialHeight,
height = Math.max(innerHeight, minHeight);
height = Math.min(height, self.options.maxHeight);
self.editor.attr("scrolling", height < self.options.maxHeight ? "no" : "auto"); // hide scrollbar firefox
innerBody.css("overflow", height < self.options.maxHeight ? "hidden" : ""); // hide scrollbar chrome
self.editor.get(0).height = height;
return self;
};
this.init = function (element, options) {
var self = this,
$form = $(element).closest("form"),
newX = (element.width || element.clientWidth || 0),
newY = (element.height || element.clientHeight || 0)
;
this.options = this.extendOptions(options);
this.original = element;
this.ui.toolbar = $(this.options.toolbarHtml);
if ($.browser.msie && parseInt($.browser.version, 10) < 8) {
this.options.autoGrow = false;
}
if (newX === 0 && element.cols) {
newX = (element.cols * 8) + 21;
}
if (newY === 0 && element.rows) {
newY = (element.rows * 16) + 16;
}
this.editor = $(window.location.protocol === "https:" ? '<iframe src="javascript:false;"></iframe>' : "<iframe></iframe>").attr("frameborder", "0");
if (this.options.iFrameClass) {
this.editor.addClass(this.options.iFrameClass);
} else {
this.editor.css({
minHeight: (newY - 6).toString() + "px",
// fix for issue 12 ( http://github.com/akzhan/jwysiwyg/issues/issue/12 )
width: (newX > 50) ? (newX - 8).toString() + "px" : ""
});
if ($.browser.msie && parseInt($.browser.version, 10) < 7) {
this.editor.css("height", newY.toString() + "px");
}
}
/**
* Automagically add id to iframe if textarea has its own when possible
* ( http://github.com/akzhan/jwysiwyg/issues/245 )
*/
if (element.id) {
var proposedId = element.id + '-wysiwyg-iframe';
if (! document.getElementById(proposedId)) {
this.editor.attr('id', proposedId);
}
}
/**
* http://code.google.com/p/jwysiwyg/issues/detail?id=96
*/
this.editor.attr("tabindex", $(element).attr("tabindex"));
this.element = $("<div/>").addClass("wysiwyg");
if (!this.options.iFrameClass) {
this.element.css({
width: (newX > 0) ? newX.toString() + "px" : "100%"
});
}
$(element).hide().before(this.element);
this.viewHTML = false;
/**
* @link http://code.google.com/p/jwysiwyg/issues/detail?id=52
*/
this.initialContent = $(element).val();
this.ui.initFrame();
if (this.options.resizeOptions && $.fn.resizable) {
this.element.resizable($.extend(true, {
alsoResize: this.editor
}, this.options.resizeOptions));
}
if (this.options.autoSave) {
$form.bind("submit.wysiwyg", function () { self.autoSaveFunction(); });
}
$form.bind("reset.wysiwyg", function () { self.resetFunction(); });
};
this.ui.initFrame = function () {
var self = this.self,
stylesheet,
growHandler,
saveHandler;
self.ui.appendControls();
self.element.append(self.ui.toolbar)
.append($("<div><!-- --></div>")
.css({
clear: "both"
}))
.append(self.editor);
self.editorDoc = self.innerDocument();
if (self.isDestroyed) {
return null;
}
self.ui.designMode();
self.editorDoc.open();
self.editorDoc.write(
self.options.html
/**
* @link http://code.google.com/p/jwysiwyg/issues/detail?id=144
*/
.replace(/INITIAL_CONTENT/, function () { return self.wrapInitialContent(); })
);
self.editorDoc.close();
$.wysiwyg.plugin.bind(self);
$(self.editorDoc).trigger("initFrame.wysiwyg");
$(self.editorDoc).bind("click.wysiwyg", function (event) {
self.ui.checkTargets(event.target ? event.target : event.srcElement);
});
/**
* @link https://github.com/akzhan/jwysiwyg/issues/251
*/
setInterval(function () {
var offset = null;
try {
var range = self.getInternalRange();
if (range) {
offset = {
range: range,
parent: $.browser.msie ? range.parentElement() : range.endContainer.parentNode,
width: ($.browser.msie ? range.boundingWidth : range.startOffset - range.endOffset) || 0
};
}
}
catch (e) { console.error(e); }
if (offset && offset.width == 0 && !self.editorDoc.rememberCommand) {
self.ui.checkTargets(offset.parent);
}
}, 400);
/**
* @link http://code.google.com/p/jwysiwyg/issues/detail?id=20
*/
$(self.original).focus(function () {
if ($(this).filter(":visible").length === 0) {
return;
}
self.ui.focus();
});
$(self.editorDoc).keydown(function (event) {
var emptyContentRegex;
if (event.keyCode === 8) { // backspace
emptyContentRegex = /^<([\w]+)[^>]*>(<br\/?>)?<\/\1>$/;
if (emptyContentRegex.test(self.getContent())) { // if content is empty
event.stopPropagation(); // prevent remove single empty tag
return false;
}
}
self.editorDoc.rememberCommand = false;
return true;
});
if (!$.browser.msie) {
$(self.editorDoc).keydown(function (event) {
var controlName;
/* Meta for Macs. tom@punkave.com */
if (event.ctrlKey || event.metaKey) {
for (controlName in self.controls) {
if (self.controls[controlName].hotkey && self.controls[controlName].hotkey.ctrl) {
if (event.keyCode === self.controls[controlName].hotkey.key) {
self.triggerControl.apply(self, [controlName, self.controls[controlName]]);
return false;
}
}
}
}
return true;
});
} else if (self.options.brIE) {
$(self.editorDoc).keydown(function (event) {
if (event.keyCode === 13) {
var rng = self.getRange();
rng.pasteHTML("<br/>");
rng.collapse(false);
rng.select();
return false;
}
return true;
});
}
if (self.options.plugins.rmFormat.rmMsWordMarkup) {
$(self.editorDoc).bind("keyup.wysiwyg", function (event) {
if (event.ctrlKey || event.metaKey) {
// CTRL + V (paste)
if (86 === event.keyCode) {
if ($.wysiwyg.rmFormat) {
if ("object" === typeof (self.options.plugins.rmFormat.rmMsWordMarkup)) {
$.wysiwyg.rmFormat.run(self, {rules: { msWordMarkup: self.options.plugins.rmFormat.rmMsWordMarkup }});
} else {
$.wysiwyg.rmFormat.run(self, {rules: { msWordMarkup: { enabled: true }}});
}
}
}
}
});
}
if (self.options.autoSave) {
$(self.editorDoc).keydown(function () { self.autoSaveFunction(); })
.keyup(function () { self.autoSaveFunction(); })
.mousedown(function () { self.autoSaveFunction(); })
.bind($.support.noCloneEvent ? "input.wysiwyg" : "paste.wysiwyg", function () { self.autoSaveFunction(); });
}
if (self.options.autoGrow) {
if (self.options.initialMinHeight !== null) {
self.ui.initialHeight = self.options.initialMinHeight;
} else {
self.ui.initialHeight = $(self.editorDoc).height();
}
$(self.editorDoc.body).css("border", "1px solid white"); // cancel margin collapsing
growHandler = function () {
self.ui.grow();
};
$(self.editorDoc).keyup(growHandler);
$(self.editorDoc).bind("editorRefresh.wysiwyg", growHandler);
// fix when content height > textarea height
self.ui.grow();
}
if (self.options.css) {
if (String === self.options.css.constructor) {
if ($.browser.msie) {
stylesheet = self.editorDoc.createStyleSheet(self.options.css);
$(stylesheet).attr({
"media": "all"
});
} else {
stylesheet = $("<link/>").attr({
"href": self.options.css,
"media": "all",
"rel": "stylesheet",
"type": "text/css"
});
$(self.editorDoc).find("head").append(stylesheet);
}
} else {
self.timers.initFrame_Css = window.setTimeout(function () {
$(self.editorDoc.body).css(self.options.css);
}, 0);
}
}
if (self.initialContent.length === 0) {
if ("function" === typeof (self.options.initialContent)) {
self.setContent(self.options.initialContent());
} else {
self.setContent(self.options.initialContent);
}
}
if (self.options.maxLength > 0) {
$(self.editorDoc).keydown(function (event) {
if ($(self.editorDoc).text().length >= self.options.maxLength && $.inArray(event.which, self.validKeyCodes) === -1) {
event.preventDefault();
}
});
}
// Support event callbacks
$.each(self.options.events, function (key, handler) {
$(self.editorDoc).bind(key + ".wysiwyg", function (event) {
// Trigger event handler, providing the event and api to
// support additional functionality.
handler.apply(self.editorDoc, [event, self]);
});
});
// restores selection properly on focus
if ($.browser.msie) {
// Event chain: beforedeactivate => focusout => blur.
// Focusout & blur fired too late to handle internalRange() in dialogs.
// When clicked on input boxes both got range = null
$(self.editorDoc).bind("beforedeactivate.wysiwyg", function () {
self.savedRange = self.getInternalRange();
});
} else {
$(self.editorDoc).bind("blur.wysiwyg", function () {
self.savedRange = self.getInternalRange();
});
}
$(self.editorDoc.body).addClass("wysiwyg");
if (self.options.events && self.options.events.save) {
saveHandler = self.options.events.save;
$(self.editorDoc).bind("keyup.wysiwyg", saveHandler);
$(self.editorDoc).bind("change.wysiwyg", saveHandler);
if ($.support.noCloneEvent) {
$(self.editorDoc).bind("input.wysiwyg", saveHandler);
} else {
$(self.editorDoc).bind("paste.wysiwyg", saveHandler);
$(self.editorDoc).bind("cut.wysiwyg", saveHandler);
}
}
/**
* XHTML5 {@link https://github.com/akzhan/jwysiwyg/issues/152}
*/
if (self.options.xhtml5 && self.options.unicode) {
var replacements = {ne:8800,le:8804,para:182,xi:958,darr:8595,nu:957,oacute:243,Uacute:218,omega:969,prime:8242,pound:163,igrave:236,thorn:254,forall:8704,emsp:8195,lowast:8727,brvbar:166,alefsym:8501,nbsp:160,delta:948,clubs:9827,lArr:8656,Omega:937,Auml:196,cedil:184,and:8743,plusmn:177,ge:8805,raquo:187,uml:168,equiv:8801,laquo:171,rdquo:8221,Epsilon:917,divide:247,fnof:402,chi:967,Dagger:8225,iacute:237,rceil:8969,sigma:963,Oslash:216,acute:180,frac34:190,lrm:8206,upsih:978,Scaron:352,part:8706,exist:8707,nabla:8711,image:8465,prop:8733,zwj:8205,omicron:959,aacute:225,Yuml:376,Yacute:221,weierp:8472,rsquo:8217,otimes:8855,kappa:954,thetasym:977,harr:8596,Ouml:214,Iota:921,ograve:242,sdot:8901,copy:169,oplus:8853,acirc:226,sup:8835,zeta:950,Iacute:205,Oacute:211,crarr:8629,Nu:925,bdquo:8222,lsquo:8216,apos:39,Beta:914,eacute:233,egrave:232,lceil:8968,Kappa:922,piv:982,Ccedil:199,ldquo:8220,Xi:926,cent:162,uarr:8593,hellip:8230,Aacute:193,ensp:8194,sect:167,Ugrave:217,aelig:230,ordf:170,curren:164,sbquo:8218,macr:175,Phi:934,Eta:919,rho:961,Omicron:927,sup2:178,euro:8364,aring:229,Theta:920,mdash:8212,uuml:252,otilde:245,eta:951,uacute:250,rArr:8658,nsub:8836,agrave:224,notin:8713,ndash:8211,Psi:936,Ocirc:212,sube:8838,szlig:223,micro:181,not:172,sup1:185,middot:183,iota:953,ecirc:234,lsaquo:8249,thinsp:8201,sum:8721,ntilde:241,scaron:353,cap:8745,atilde:227,lang:10216,__replacement:65533,isin:8712,gamma:947,Euml:203,ang:8736,upsilon:965,Ntilde:209,hearts:9829,Alpha:913,Tau:932,spades:9824,dagger:8224,THORN:222,"int":8747,lambda:955,Eacute:201,Uuml:220,infin:8734,rlm:8207,Aring:197,ugrave:249,Egrave:200,Acirc:194,rsaquo:8250,ETH:208,oslash:248,alpha:945,Ograve:210,Prime:8243,mu:956,ni:8715,real:8476,bull:8226,beta:946,icirc:238,eth:240,prod:8719,larr:8592,ordm:186,perp:8869,Gamma:915,reg:174,ucirc:251,Pi:928,psi:968,tilde:732,asymp:8776,zwnj:8204,Agrave:192,deg:176,AElig:198,times:215,Delta:916,sim:8764,Otilde:213,Mu:924,uArr:8657,circ:710,theta:952,Rho:929,sup3:179,diams:9830,tau:964,Chi:935,frac14:188,oelig:339,shy:173,or:8744,dArr:8659,phi:966,iuml:239,Lambda:923,rfloor:8971,iexcl:161,cong:8773,ccedil:231,Icirc:206,frac12:189,loz:9674,rarr:8594,cup:8746,radic:8730,frasl:8260,euml:235,OElig:338,hArr:8660,Atilde:195,Upsilon:933,there4:8756,ouml:246,oline:8254,Ecirc:202,yacute:253,auml:228,permil:8240,sigmaf:962,iquest:191,empty:8709,pi:960,Ucirc:219,supe:8839,Igrave:204,yen:165,rang:10217,trade:8482,lfloor:8970,minus:8722,Zeta:918,sub:8834,epsilon:949,yuml:255,Sigma:931,Iuml:207,ocirc:244};
self.events.bind("getContent", function (text) {
return text.replace(/&(?:amp;)?(?!amp|lt|gt|quot)([a-z][a-z0-9]*);/gi, function (str, p1) {
if (!replacements[p1]) {
p1 = p1.toLowerCase();
if (!replacements[p1]) {
p1 = "__replacement";
}
}
var num = replacements[p1];
/* Numeric return if ever wanted: return replacements[p1] ? "&#"+num+";" : ""; */
return String.fromCharCode(num);
});
});
}
$(self.original).trigger('ready.jwysiwyg', [self.editorDoc, self]);
};
this.innerDocument = function () {
var element = this.editor.get(0);
if (element.nodeName.toLowerCase() === "iframe") {
if (element.contentDocument) { // Gecko
return element.contentDocument;
} else if (element.contentWindow) { // IE
return element.contentWindow.document;
}
if (this.isDestroyed) {
return null;
}
console.error("Unexpected error in innerDocument");
/*
return ( $.browser.msie )
? document.frames[element.id].document
: element.contentWindow.document // contentDocument;
*/
}
return element;
};
this.insertHtml = function (szHTML) {
var img, range;
if (!szHTML || szHTML.length === 0) {
return this;
}
if ($.browser.msie) {
this.ui.focus();
this.editorDoc.execCommand("insertImage", false, "#jwysiwyg#");
img = this.getElementByAttributeValue("img", "src", "#jwysiwyg#");
if (img) {
$(img).replaceWith(szHTML);
}
} else {
if ($.browser.mozilla) { // @link https://github.com/akzhan/jwysiwyg/issues/50
if (1 === $(szHTML).length) {
range = this.getInternalRange();
range.deleteContents();
range.insertNode($(szHTML).get(0));
} else {
this.editorDoc.execCommand("insertHTML", false, szHTML);
}
} else {
if (!this.editorDoc.execCommand("insertHTML", false, szHTML)) {
this.editor.focus();
/* :TODO: place caret at the end
if (window.getSelection) {
} else {
}
this.editor.focus();
*/
this.editorDoc.execCommand("insertHTML", false, szHTML);
}
}
}
this.saveContent();
return this;
};
//check allowed properties
this.parseControls = function () {
var self = this;
$.each(this.options.controls, function (controlName, control) {
$.each(control, function (propertyName) {
if (-1 === $.inArray(propertyName, self.availableControlProperties)) {
throw controlName + '["' + propertyName + '"]: property "' + propertyName + '" not exists in Wysiwyg.availableControlProperties';
}
});
});
if (this.options.parseControls) { //user callback
return this.options.parseControls.call(this);
}
return this.options.controls;
};
this.removeFormat = function () {
if ($.browser.msie) {
this.ui.focus();
}
if (this.options.removeHeadings) {
this.editorDoc.execCommand("formatBlock", false, "<p>"); // remove headings
}
this.editorDoc.execCommand("removeFormat", false, null);
this.editorDoc.execCommand("unlink", false, null);
if ($.wysiwyg.rmFormat && $.wysiwyg.rmFormat.enabled) {
if ("object" === typeof (this.options.plugins.rmFormat.rmMsWordMarkup)) {
$.wysiwyg.rmFormat.run(this, {rules: { msWordMarkup: this.options.plugins.rmFormat.rmMsWordMarkup }});
} else {
$.wysiwyg.rmFormat.run(this, {rules: { msWordMarkup: { enabled: true }}});
}
}
return this;
};
this.ui.removeHoverClass = function () {
$(this).removeClass("wysiwyg-button-hover");
};
this.resetFunction = function () {
this.setContent(this.initialContent);
};
this.saveContent = function () {
if (this.viewHTML)
{
return; // no need
}
if (this.original) {
var content, newContent;
content = this.getContent();
if (this.options.rmUnwantedBr) {
content = content.replace(/<br\/?>$/, "");
}
if (this.options.replaceDivWithP) {
newContent = $("<div/>").addClass("temp").append(content);
newContent.children("div").each(function () {
var element = $(this), p = element.find("p"), i;
if (0 === p.length) {
p = $("<p></p>");
if (this.attributes.length > 0) {
for (i = 0; i < this.attributes.length; i += 1) {
p.attr(this.attributes[i].name, element.attr(this.attributes[i].name));
}
}
p.append(element.html());
element.replaceWith(p);
}
});
content = newContent.html();
}
$(this.original).val(content);
if (this.options.events && this.options.events.save) {
this.options.events.save.call(this);
}
}
return this;
};
this.setContent = function (newContent) {
this.editorDoc.body.innerHTML = newContent;
this.saveContent();
return this;
};
this.triggerControl = function (name, control) {
var cmd = control.command || name, //command directly for designMode=on iframe (this.editorDoc)
args = control["arguments"] || [];
if (control.exec) {
control.exec.apply(this); //custom exec function in control, allows DOM changing
} else {
this.ui.focus();
this.ui.withoutCss(); //disable style="" attr inserting in mozzila's designMode
// when click <Cut>, <Copy> or <Paste> got "Access to XPConnect service denied" code: "1011"
// in Firefox untrusted JavaScript is not allowed to access the clipboard
try {
this.editorDoc.execCommand(cmd, false, args);
} catch (e) {
console.error(e);
}
}
if (this.options.autoSave) {
this.autoSaveFunction();
}
};
this.triggerControlCallback = function (name) {
$(window).trigger("trigger-" + name + ".wysiwyg", [this]);
};
this.ui.withoutCss = function () {
var self = this.self;
if ($.browser.mozilla) {
try {
self.editorDoc.execCommand("styleWithCSS", false, false);
} catch (e) {
try {
self.editorDoc.execCommand("useCSS", false, true);
} catch (e2) {
}
}
}
return self;
};
this.wrapInitialContent = function () {
var content = this.initialContent,
found = content.match(/<\/?p>/gi);
if (!found) {
return "<p>" + content + "</p>";
} else {
// :TODO: checking/replacing
}
return content;
};
}
/*
* Wysiwyg namespace: public properties and methods
*/
$.wysiwyg = {
messages: {
noObject: "Something goes wrong, check object"
},
/**
* Custom control support by Alec Gorge ( http://github.com/alecgorge )
*/
addControl: function (object, name, settings) {
return object.each(function () {
var oWysiwyg = $(this).data("wysiwyg"),
customControl = {},
toolbar;
if (!oWysiwyg) {
return this;
}
customControl[name] = $.extend(true, {visible: true, custom: true}, settings);
$.extend(true, oWysiwyg.options.controls, customControl);
// render new toolbar
toolbar = $(oWysiwyg.options.toolbarHtml);
oWysiwyg.ui.toolbar.replaceWith(toolbar);
oWysiwyg.ui.toolbar = toolbar;
oWysiwyg.ui.appendControls();
});
},
clear: function (object) {
return object.each(function () {
var oWysiwyg = $(this).data("wysiwyg");
if (!oWysiwyg) {
return this;
}
oWysiwyg.setContent("");
});
},
console: console, // let our console be available for extensions
destroy: function (object) {
return object.each(function () {
var oWysiwyg = $(this).data("wysiwyg");
if (!oWysiwyg) {
return this;
}
oWysiwyg.destroy();
});
},
"document": function (object) {
// no chains because of return
var oWysiwyg = object.data("wysiwyg");
if (!oWysiwyg) {
return undefined;
}
return $(oWysiwyg.editorDoc);
},
getContent: function (object) {
// no chains because of return
var oWysiwyg = object.data("wysiwyg");
if (!oWysiwyg) {
return undefined;
}
return oWysiwyg.getContent();
},
init: function (object, options) {
return object.each(function () {
var opts = $.extend(true, {}, options),
obj;
// :4fun:
// remove this textarea validation and change line in this.saveContent function
// $(this.original).val(content); to $(this.original).html(content);
// now you can make WYSIWYG editor on h1, p, and many more tags
if (("textarea" !== this.nodeName.toLowerCase()) || $(this).data("wysiwyg")) {
return;
}
obj = new Wysiwyg();
obj.init(this, opts);
$.data(this, "wysiwyg", obj);
$(obj.editorDoc).trigger("afterInit.wysiwyg");
});
},
insertHtml: function (object, szHTML) {
return object.each(function () {
var oWysiwyg = $(this).data("wysiwyg");
if (!oWysiwyg) {
return this;
}
oWysiwyg.insertHtml(szHTML);
});
},
plugin: {
listeners: {},
bind: function (Wysiwyg) {
var self = this;
$.each(this.listeners, function (action, handlers) {
var i, plugin;
for (i = 0; i < handlers.length; i += 1) {
plugin = self.parseName(handlers[i]);
$(Wysiwyg.editorDoc).bind(action + ".wysiwyg", {plugin: plugin}, function (event) {
$.wysiwyg[event.data.plugin.name][event.data.plugin.method].apply($.wysiwyg[event.data.plugin.name], [Wysiwyg]);
});
}
});
},
exists: function (name) {
var plugin;
if ("string" !== typeof (name)) {
return false;
}
plugin = this.parseName(name);
if (!$.wysiwyg[plugin.name] || !$.wysiwyg[plugin.name][plugin.method]) {
return false;
}
return true;
},
listen: function (action, handler) {
var plugin;
plugin = this.parseName(handler);
if (!$.wysiwyg[plugin.name] || !$.wysiwyg[plugin.name][plugin.method]) {
return false;
}
if (!this.listeners[action]) {
this.listeners[action] = [];
}
this.listeners[action].push(handler);
return true;
},
parseName: function (name) {
var elements;
if ("string" !== typeof (name)) {
return false;
}
elements = name.split(".");
if (2 > elements.length) {
return false;
}
return {name: elements[0], method: elements[1]};
},
register: function (data) {
if (!data.name) {
console.error("Plugin name missing");
}
$.each($.wysiwyg, function (pluginName) {
if (pluginName === data.name) {
console.error("Plugin with name '" + data.name + "' was already registered");
}
});
$.wysiwyg[data.name] = data;
return true;
}
},
removeFormat: function (object) {
return object.each(function () {
var oWysiwyg = $(this).data("wysiwyg");
if (!oWysiwyg) {
return this;
}
oWysiwyg.removeFormat();
});
},
save: function (object) {
return object.each(function () {
var oWysiwyg = $(this).data("wysiwyg");
if (!oWysiwyg) {
return this;
}
oWysiwyg.saveContent();
});
},
selectAll: function (object) {
var oWysiwyg = object.data("wysiwyg"), oBody, oRange, selection;
if (!oWysiwyg) {
return this;
}
oBody = oWysiwyg.editorDoc.body;
if (window.getSelection) {
selection = oWysiwyg.getInternalSelection();
selection.selectAllChildren(oBody);
} else {
oRange = oBody.createTextRange();
oRange.moveToElementText(oBody);
oRange.select();
}
},
setContent: function (object, newContent) {
return object.each(function () {
var oWysiwyg = $(this).data("wysiwyg");
if (!oWysiwyg) {
return this;
}
oWysiwyg.setContent(newContent);
});
},
triggerControl: function (object, controlName) {
return object.each(function () {
var oWysiwyg = $(this).data("wysiwyg");
if (!oWysiwyg) {
return this;
}
if (!oWysiwyg.controls[controlName]) {
console.error("Control '" + controlName + "' not exists");
}
oWysiwyg.triggerControl.apply(oWysiwyg, [controlName, oWysiwyg.controls[controlName]]);
});
},
support: {
prop: supportsProp
},
utils: {
extraSafeEntities: [["<", ">", "'", '"', " "], [32]],
encodeEntities: function (str) {
var self = this, aStr, aRet = [];
if (this.extraSafeEntities[1].length === 0) {
$.each(this.extraSafeEntities[0], function (i, ch) {
self.extraSafeEntities[1].push(ch.charCodeAt(0));
});
}
aStr = str.split("");
$.each(aStr, function (i) {
var iC = aStr[i].charCodeAt(0);
if ($.inArray(iC, self.extraSafeEntities[1]) && (iC < 65 || iC > 127 || (iC > 90 && iC < 97))) {
aRet.push('&#' + iC + ';');
} else {
aRet.push(aStr[i]);
}
});
return aRet.join('');
}
}
};
/**
* Unifies dialog methods to allow custom implementations
*
* Events:
* * afterOpen
* * beforeShow
* * afterShow
* * beforeHide
* * afterHide
* * beforeClose
* * afterClose
*
* Example:
* var dialog = new ($.wysiwyg.dialog)($('#idToTextArea').data('wysiwyg'), {"title": "Test", "content": "form data, etc."});
*
* dialog.bind("afterOpen", function () { alert('you should see a dialog behind this one!'); });
*
* dialog.open();
*
*
*/
$.wysiwyg.dialog = function (jWysiwyg, opts) {
var theme = (jWysiwyg && jWysiwyg.options && jWysiwyg.options.dialog) ? jWysiwyg.options.dialog : (opts.theme ? opts.theme : "default"),
obj = new $.wysiwyg.dialog.createDialog(theme),
that = this,
$that = $(that);
this.options = {
"modal": true,
"draggable": true,
"title": "Title",
"content": "Content",
"width": "auto",
"height": "auto",
"zIndex": 2000,
"open": false,
"close": false
};
this.isOpen = false;
$.extend(this.options, opts);
this.object = obj;
// Opens a dialog with the specified content
this.open = function () {
this.isOpen = true;
obj.init.apply(that, []);
var $dialog = obj.show.apply(that, []);
$that.trigger("afterOpen", [$dialog]);
};
this.show = function () {
this.isOpen = true;
$that.trigger("beforeShow");
var $dialog = obj.show.apply(that, []);
$that.trigger("afterShow");
};
this.hide = function () {
this.isOpen = false;
$that.trigger("beforeHide");
var $dialog = obj.hide.apply(that, []);
$that.trigger("afterHide", [$dialog]);
};
// Closes the dialog window.
this.close = function () {
this.isOpen = false;
var $dialog = obj.hide.apply(that, []);
$that.trigger("beforeClose", [$dialog]);
obj.destroy.apply(that, []);
$that.trigger("afterClose", [$dialog]);
};
if (this.options.open) {
$that.bind("afterOpen", this.options.open);
}
if (this.options.close) {
$that.bind("afterClose", this.options.close);
}
return this;
};
// "Static" Dialog methods.
$.extend(true, $.wysiwyg.dialog, {
_themes : {}, // sample {"Theme Name": object}
_theme : "", // the current theme
register : function(name, obj) {
$.wysiwyg.dialog._themes[name] = obj;
},
deregister : function (name) {
delete $.wysiwyg.dialog._themes[name];
},
createDialog : function (name) {
return new ($.wysiwyg.dialog._themes[name]);
},
getDimensions : function () {
var width = document.body.scrollWidth,
height = document.body.scrollHeight;
if ($.browser.opera) {
height = Math.max(
$(document).height(),
$(window).height(),
document.documentElement.clientHeight);
}
return [width, height];
}
});
$(function () { // need access to jQuery UI stuff.
if (jQuery.ui) {
$.wysiwyg.dialog.register("jqueryui", function () {
var that = this;
this._$dialog = null;
this.init = function() {
var abstractDialog = this,
content = this.options.content;
if (typeof content === 'object') {
if (typeof content.html === 'function') {
content = content.html();
} else if(typeof content.toString === 'function') {
content = content.toString();
}
}
that._$dialog = $('<div></div>').attr('title', this.options.title).html(content);
var dialogHeight = this.options.height == 'auto' ? 300 : this.options.height,
dialogWidth = this.options.width == 'auto' ? 450 : this.options.width;
// console.log(that._$dialog);
that._$dialog.dialog({
modal: this.options.modal,
draggable: this.options.draggable,
height: dialogHeight,
width: dialogWidth
});
return that._$dialog;
};
this.show = function () {
that._$dialog.dialog("open");
return that._$dialog;
};
this.hide = function () {
that._$dialog.dialog("close");
return that._$dialog;
};
this.destroy = function() {
that._$dialog.dialog("destroy");
return that._$dialog;
};
});
}
$.wysiwyg.dialog.register("default", function () {
var that = this;
this._$dialog = null;
this.init = function() {
var abstractDialog = this,
content = this.options.content;
if (typeof content === 'object') {
if(typeof content.html === 'function') {
content = content.html();
}
else if(typeof content.toString === 'function') {
content = content.toString();
}
}
that._$dialog = $('<div class="wysiwyg-dialog"></div>').css({"z-index": this.options.zIndex});
var $topbar = $('<div class="wysiwyg-dialog-topbar"><div class="wysiwyg-dialog-close-wrapper"></div><div class="wysiwyg-dialog-title">'+this.options.title+'</div></div>');
var $link = $('<a href="#" class="wysiwyg-dialog-close-button">X</a>');
$link.click(function () {
abstractDialog.close(); // this is important it makes sure that is close from the abstract $.wysiwyg.dialog instace, not just locally
});
$topbar.find('.wysiwyg-dialog-close-wrapper').prepend($link);
var $dcontent = $('<div class="wysiwyg-dialog-content">'+content+'</div>');
that._$dialog.append($topbar).append($dcontent);
// Set dialog's height & width, and position it correctly:
var dialogHeight = this.options.height == 'auto' ? 300 : this.options.height,
dialogWidth = this.options.width == 'auto' ? 450 : this.options.width;
that._$dialog.hide().css({
"width": dialogWidth,
"height": dialogHeight,
"left": (($(window).width() - dialogWidth) / 2),
"top": (($(window).height() - dialogHeight) / 3)
});
$("body").append(that._$dialog);
return that._$dialog;
};
this.show = function () {
// Modal feature:
if (this.options.modal) {
var dimensions = $.wysiwyg.dialog.getDimensions(),
wrapper = $('<div class="wysiwyg-dialog-modal-div"></div>')
.css({"width": dimensions[0], "height": dimensions[1]});
that._$dialog.wrap(wrapper);
}
// Draggable feature:
if (this.options.draggable) {
var mouseDown = false;
that._$dialog.find("div.wysiwyg-dialog-topbar").bind("mousedown", function (e) {
e.preventDefault();
$(this).css({ "cursor": "move" });
var $topbar = $(this),
_dialog = $(this).parents(".wysiwyg-dialog"),
offsetX = (e.pageX - parseInt(_dialog.css("left"), 10)),
offsetY = (e.pageY - parseInt(_dialog.css("top"), 10));
mouseDown = true;
$(this).css({ "cursor": "move" });
$(document).bind("mousemove", function (e) {
e.preventDefault();
if (mouseDown) {
_dialog.css({
"top": (e.pageY - offsetY),
"left": (e.pageX - offsetX)
});
}
}).bind("mouseup", function (e) {
e.preventDefault();
mouseDown = false;
$topbar.css({ "cursor": "auto" });
$(document).unbind("mousemove").unbind("mouseup");
});
});
}
that._$dialog.show();
return that._$dialog;
};
this.hide = function () {
that._$dialog.hide();
return that._$dialog;
};
this.destroy = function() {
// Modal feature:
if (this.options.modal) {
that._$dialog.unwrap();
}
// Draggable feature:
if (this.options.draggable) {
that._$dialog.find("div.wysiwyg-dialog-topbar").unbind("mousedown");
}
that._$dialog.remove();
return that._$dialog;
};
});
});
// end Dialog
$.fn.wysiwyg = function (method) {
var args = arguments, plugin;
if ("undefined" !== typeof $.wysiwyg[method]) {
// set argument object to undefined
args = Array.prototype.concat.call([args[0]], [this], Array.prototype.slice.call(args, 1));
return $.wysiwyg[method].apply($.wysiwyg, Array.prototype.slice.call(args, 1));
} else if ("object" === typeof method || !method) {
Array.prototype.unshift.call(args, this);
return $.wysiwyg.init.apply($.wysiwyg, args);
} else if ($.wysiwyg.plugin.exists(method)) {
plugin = $.wysiwyg.plugin.parseName(method);
args = Array.prototype.concat.call([args[0]], [this], Array.prototype.slice.call(args, 1));
return $.wysiwyg[plugin.name][plugin.method].apply($.wysiwyg[plugin.name], Array.prototype.slice.call(args, 1));
} else {
console.error("Method '" + method + "' does not exist on jQuery.wysiwyg.\nTry to include some extra controls or plugins");
}
};
$.fn.getWysiwyg = function () {
return this.data("wysiwyg");
};
})(jQuery); 